package com.mwl.gateway.lb;

import com.mwl.gateway.constant.GlobalVariable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * description: 负载均衡器
 *
 * @author maweilong
 **/
public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private static final Log log = LogFactory.getLog(GrayLoadBalancer.class);
    private final String serviceId;
    /**
     * 位置，下标
     */
    private final AtomicInteger position;
    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        //随机进行设置一个值
        this.position = new AtomicInteger(new Random().nextInt(1000));
    }

    /**
     * Choose the next server based on the load balancing algorithm.
     *
     * @param request - an input request
     * @return - mono of response
     */
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        // 提供备选的服务实例列表 NoopServiceInstanceListSupplier::new
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
                .getIfAvailable(NoopServiceInstanceListSupplier::new);
        // 选择服务实例
        return supplier.get(request).next().map((serviceInstances) ->
                this.processInstanceResponse(supplier, serviceInstances, request)
        );
    }

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
                                                              List<ServiceInstance> serviceInstances,
                                                              Request<?> request) {
        // 从备选的服务列表中选择一个具体的服务实例
        Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances, request);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request<?> request) {
        log.info("ssss");
        // 实例为空   首先进行实例判空
        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                //判空后 给予警告
                log.warn("No servers available for service: " + this.serviceId);
            }
            //返回响应
            return new EmptyResponse();
        } else { // 服务不为空
            // 灰度节点的业务实现
            // 0.得到 Request 对象[通过方法参数的传递得到此对象]
            // 1.从 Request 对象的 Header 中得到灰度标签
            RequestDataContext requestContext = (RequestDataContext) request.getContext();
            HttpHeaders headers = requestContext.getClientRequest().getHeaders();
            List<String> grayTagHeaders = headers.get(GlobalVariable.GRAY_TAG);
            List<ServiceInstance> filteredInstances;
            if (!CollectionUtils.isEmpty(grayTagHeaders)) {
                // 灰度节点
                String grayTag = grayTagHeaders.get(0);
                filteredInstances = instances.stream()
                        .filter(i -> i.getMetadata() != null && // 避免空指针异常
                                grayTag.equals(i.getMetadata().get(GlobalVariable.GRAY_TAG)))
                        .toList();
            }else {
                // 正式节点
                filteredInstances = instances.stream()
                        .filter(i -> i.getMetadata().get(GlobalVariable.GRAY_TAG) == null)
                        .toList();
            }
            if (filteredInstances.isEmpty()){
                return new EmptyResponse();
            }
            // 3.使用负载均衡算法选择上一步列表中的某一个节点
            int indexPosition = this.position.incrementAndGet() & Integer.MAX_VALUE;
            ServiceInstance instance = instances.get(indexPosition % filteredInstances.size());
            return new DefaultResponse(instance);
        }
    }


}
